home *** CD-ROM | disk | FTP | other *** search
- /*
- tcp_raw.c
-
- Copyright (c) 2000 Dug Song <dugsong@monkey.org>
-
- $Id: tcp_raw.c,v 1.2 2000/05/18 20:40:33 dugsong Exp $
- */
-
- #include "config.h"
- #include <sys/types.h>
- #include <sys/uio.h>
- #include <time.h>
- #ifdef HAVE_ERR_H
- #include <err.h>
- #endif
- #include <libnet.h>
- #include "options.h"
- #include "tcp_raw.h"
-
- struct tha {
- u_long src;
- u_long dst;
- u_long port;
- };
-
- struct tcp_seg {
- u_long seq;
- u_char *data;
- int len;
- };
-
- struct tcp_conn {
- struct tha tha;
- time_t mtime;
- struct tcp_seg *seg;
- int segcnt;
- int segmax;
- struct tcp_conn *next;
- };
-
- #define TCP_TIMEOUT 60
- #define TCP_HASHSIZE 919
-
- static struct tcp_conn conntab[TCP_HASHSIZE];
-
- static int
- tcp_seg_compare(const void *a, const void *b)
- {
- struct tcp_seg *sa, *sb;
-
- sa = (struct tcp_seg *) a;
- sb = (struct tcp_seg *) b;
-
- if (sa->seq < sb->seq)
- return (-1);
- else if (sa->seq > sb->seq)
- return (1);
- else return (0);
- }
-
- static void
- tcp_raw_delete(struct tcp_conn *conn)
- {
- struct tcp_conn *hold;
- int i;
-
- if (conn->next != NULL) {
- for (i = 0; i < conn->segcnt; i++) {
- if (conn->seg[i].data)
- free(conn->seg[i].data);
- }
- free(conn->seg);
- conn->seg = NULL;
- conn->segcnt = conn->segmax = 0;
-
- if (conn->next->next != NULL) {
- hold = conn->next;
- *conn = *conn->next;
- free(hold);
- }
- else {
- free(conn->next);
- conn->next = NULL;
- }
- }
- }
-
- static struct iovec *
- tcp_raw_reassemble(struct tcp_conn *conn, int minlen)
- {
- struct iovec *iov;
- int i, len;
-
- len = 0;
-
- for (i = 0; i < conn->segcnt; i++)
- len += conn->seg[i].len;
-
- if (len < minlen)
- return (NULL);
-
- if ((iov = calloc(1, sizeof(*iov))) == NULL)
- err(1, "tcp_raw_input: calloc");
-
- qsort(conn->seg, conn->segcnt, sizeof(*conn->seg), tcp_seg_compare);
-
- for (i = 0; i < conn->segcnt; i++) {
- len = conn->seg[i].len;
-
- if (iov->iov_base == NULL) {
- if ((iov->iov_base = malloc(len)) == NULL)
- err(1, "tcp_raw_reassemble: malloc");
- }
- else {
- iov->iov_base = realloc(iov->iov_base,
- iov->iov_len + len);
- if (iov->iov_base == NULL)
- err(1, "tcp_raw_reassemble: realloc");
- }
- memcpy(iov->iov_base + iov->iov_len, conn->seg[i].data, len);
- iov->iov_len += len;
- }
- return (iov);
- }
-
- struct iovec *
- tcp_raw_input(struct ip *ip, struct tcphdr *tcp, int len)
- {
- struct tha tha;
- struct tcp_conn *conn;
- struct tcp_seg seg;
- struct iovec *iov;
- u_short cksum;
- u_char *buf;
- int tcp_hl = tcp->th_off * 4;
-
- /* Verify TCP checksum. */
- cksum = tcp->th_sum;
- libnet_do_checksum((u_char *) ip, IPPROTO_TCP, len);
-
- if (cksum != tcp->th_sum)
- return (NULL);
-
- tha.src = ip->ip_src.s_addr;
- tha.dst = ip->ip_dst.s_addr;
- tha.port = ntohs(tcp->th_sport) << 16 | ntohs(tcp->th_dport);
-
- buf = (u_char *)tcp + tcp_hl;
- len -= tcp_hl;
- iov = NULL;
-
- /* Find half-duplex stream associated with this segment. */
- for (conn = &conntab[tha.port % TCP_HASHSIZE];
- conn->next != NULL; conn = conn->next) {
- if (memcmp((char *)&tha, (char *)&conn->tha, sizeof(tha)) == 0)
- break;
- }
- /* Process by TCP flags. */
- if (conn->next == NULL) {
- if (tcp->th_flags & TH_SYN) {
- if (conn->next == NULL) {
- if ((conn->next = (struct tcp_conn *)
- calloc(1, sizeof(*conn))) == NULL)
- err(1, "tcp_raw_input: calloc");
- }
- conn->tha = tha;
-
- if (conn->seg == NULL) {
- if ((conn->seg = (struct tcp_seg *)
- malloc(sizeof(seg) * 128)) == NULL)
- err(1, "tcp_raw_input: malloc");
- conn->segmax = 128;
- }
- }
- }
- else if (tcp->th_flags & TH_FIN || tcp->th_flags & TH_RST) {
- iov = tcp_raw_reassemble(conn, 1);
- }
- else if (tcp->th_flags & TH_ACK && len > 0) {
- if ((seg.data = (u_char *) malloc(len)) == NULL)
- err(1, "tcp_raw_input: malloc");
- memcpy(seg.data, buf, len);
- seg.seq = ntohl(tcp->th_seq);
- seg.len = len;
-
- if (conn->segcnt == conn->segmax) {
- if ((conn->seg = (struct tcp_seg *)
- realloc(conn->seg, (conn->segmax * 2) *
- sizeof(seg))) == NULL)
- err(1, "tcp_raw_input: realloc");
- conn->segmax *= 2;
- }
- conn->seg[conn->segcnt++] = seg;
-
- iov = tcp_raw_reassemble(conn, Opt_snaplen);
- }
- conn->mtime = time(NULL);
-
- /* If we successfully reassembled the stream, delete its entry. */
- if (iov != NULL) {
- tcp_raw_delete(conn);
- }
- return (iov);
- }
-
- void
- tcp_raw_timeout(int timeout, tcp_raw_callback_t callback)
- {
- struct tcp_conn *conn;
- struct iovec *iov;
- time_t now;
- int i;
-
- now = time(NULL);
-
- for (i = 0; i < TCP_HASHSIZE; i++) {
- for (conn = &conntab[i]; conn != NULL && conn->next != NULL;
- conn = conn->next) {
- if (now - conn->mtime > timeout) {
- iov = tcp_raw_reassemble(conn, 1);
-
- if (iov != NULL) {
- callback(conn->tha.src, conn->tha.dst,
- conn->tha.port >> 16,
- conn->tha.port & 0xffff,
- iov->iov_base, iov->iov_len);
- free(iov->iov_base);
- free(iov);
- }
- tcp_raw_delete(conn);
- }
- }
- }
- }
-
-